package com.zlc.workflow.core.instance.cmd;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.zlc.common.common.constant.WorkflowConstant;
import com.zlc.common.common.exception.WorkflowException;
import com.zlc.common.core.process.model.entity.StoneTask;
import com.zlc.common.core.process.model.vo.StoneProcessInstanceHistoryVo;
import com.zlc.common.core.process.model.vo.StoneProcessInstanceVo;
import com.zlc.common.core.process.model.vo.StoneProcessNodeVo;
import com.zlc.common.core.process.model.vo.StoneProcessVo;
import com.zlc.common.core.process.model.vo.StoneTaskHistoryVo;
import com.zlc.common.core.process.model.vo.StoneTaskVo;
import com.zlc.common.core.process.service.*;
import com.zlc.common.core.user.model.vo.MayanPlatUserVo;
import com.zlc.common.core.user.service.MayanPlatUserService;
import com.zlc.common.util.AssertUtils;
import com.zlc.common.util.BeanCopyUtils;
import com.zlc.common.util.JsonHelper;
import com.zlc.common.util.PlatUtils;
import com.zlc.workflow.constant.EventTimeEnum;
import com.zlc.workflow.constant.NodeType;
import com.zlc.workflow.constant.ProcessLogTypeEnum;
import com.zlc.workflow.core.instance.model.EventParam;
import com.zlc.workflow.core.instance.model.NextNode;
import com.zlc.workflow.core.instance.model.NodeApproveUser;
import com.zlc.workflow.core.instance.model.SubmitFlowParam;
import com.zlc.workflow.core.instance.model.WorkflowDetail;
import com.zlc.workflow.core.instance.model.WorkflowTask;
import com.zlc.workflow.core.instance.service.EventService;
import com.zlc.workflow.flow.NodeModel;
import com.zlc.workflow.flow.NodeUtils;
import com.zlc.workflow.flow.TaskModel;
import com.zlc.workflow.flow.node.AutoNode;
import com.zlc.workflow.flow.node.EndNode;
import com.zlc.workflow.flow.node.JoinNode;
import com.zlc.workflow.flow.node.Path;
import com.zlc.workflow.flow.node.TaskNode;
import com.zlc.workflow.flow.node.WaitNode;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.ObjectUtils;

import java.util.*;
import java.util.stream.Collectors;

import static com.zlc.common.common.constant.WorkflowConstant.EVENT_TASK_ARRIVE;
import static com.zlc.common.common.constant.WorkflowConstant.EVENT_TASK_END;
import static com.zlc.common.common.constant.WorkflowConstant.TASK_OPERATE_TYPE_AUTO_SUBMIT;
import static com.zlc.common.common.constant.WorkflowConstant.TASK_OPERATE_TYPE_SUBMIT;
import static com.zlc.common.common.constant.WorkflowConstant.TASK_STATUS_COMPLETE;
import static com.zlc.common.common.constant.WorkflowConstant.TASK_STATUS_PROCESSING;

/**
 * 提交流程
 */
@Slf4j
public class SubmitCmd extends AbstractCmd<WorkflowDetail> {


    @Autowired
    private MayanPlatUserService mayanPlatUserService;
    @Autowired
    private StoneProcessService stoneProcessService;
    @Autowired
    private StoneProcessInstanceService stoneProcessInstanceService;
    @Autowired
    private StoneProcessInstanceHistoryService stoneProcessInstanceHistoryService;
    @Autowired
    private StoneTaskService stoneTaskService;
    @Autowired
    private StoneTaskHistoryService stoneTaskHistoryService;
    @Autowired
    private StoneProcessNodeService stoneProcessNodeService;
    @Autowired
    private EventService eventService;
    @Autowired
    private StoneProcessLogService stoneProcessLogService;
    @Autowired
    private StoneProcessVariableConfigService stoneProcessVariableConfigService;

    private final  SubmitFlowParam workflowParam;

    private final String account;

    private final  String processId;

    private final MayanPlatUserVo nowUser;

    private final String processInstanceId;

    private final String taskId;

    private final Map<String,Object> param;

    private final StoneProcessVo processVo;

    private final StoneTaskVo taskVo;

    private final StoneProcessInstanceVo instanceVo;

    private boolean taskNextContinue = false;

    private boolean changeInstanceFlag = false;

    private final TaskNode taskNode;

    private final  WorkflowDetail result = new WorkflowDetail();

    private final List<StoneProcessNodeVo> stoneProcessNodeVos;

    /**
     * 配置变量
     */
    private Map<String,String> variableConfigs = new HashMap<>();

    //提交路线
    private List<String> paths = new ArrayList<>();

    /**
     * 获取当前命令对象的所有带 @Autowired注解的属性，然后从spring上下文中获取到注入进去
     *
     */
    public SubmitCmd(Object params) {
        super(params);
        this.workflowParam = (SubmitFlowParam) params;
        this.processId = workflowParam.getProcessId();
        this.account = workflowParam.getAccount();
        this.processInstanceId = workflowParam.getProcessInstanceId();
        this.taskId = workflowParam.getTaskId();
        AssertUtils.isTrue(!ObjectUtils.isEmpty(this.workflowParam.getSuggest()),"审批意见不可为空！");
        AssertUtils.isTrue(!ObjectUtils.isEmpty(processInstanceId),"流程实例ID缺失");
        AssertUtils.isTrue(!ObjectUtils.isEmpty(taskId),"流程任务ID缺失");
        this.instanceVo = stoneProcessInstanceService.getByProcessInstanceId(processInstanceId);
        AssertUtils.isTrue(!ObjectUtils.isEmpty(instanceVo),"流程实例ID错误");
        this.taskVo = stoneTaskService.getByTaskId(taskId);
        AssertUtils.isTrue(!ObjectUtils.isEmpty(taskVo),"流程任务ID错误");

        result.setProcessInstanceId(processInstanceId);
        this.param = workflowParam.getFormData();
        AssertUtils.isTrue(!ObjectUtils.isEmpty(workflowParam.getAccount()),"操作人不可为空");
        this.nowUser = mayanPlatUserService.queryByUserAccount(workflowParam.getAccount());
        AssertUtils.isTrue(!ObjectUtils.isEmpty(this.nowUser),"操作人不存在");
        if(!NodeType.WAIT.value().equals(taskVo.getNodeType())){
            AssertUtils.isTrue(account.equals(taskVo.getActorId()),"无权操作");
        }

        this.processVo = stoneProcessService.getByProcessId(processId);
        String content = NodeUtils.getProcessContent(processVo.getContent());
        this.taskNode = NodeUtils.getTaskNode(content, processId, taskVo.getNodeId());

        this.stoneProcessNodeVos = stoneProcessNodeService.getByProcessId(this.workflowParam.getProcessId());
    }


    @Override
    public WorkflowDetail execute() {
        //完成当前任务
        completeNowTask();
        //执行提交
        submit();
        //返回
        loadWorkflowDetail();
        return this.result;
    }

    /**
     * 执行后续任务
     */
    private void submit(){
        //获取任务后续节点
        String pathNodeId = taskNode.path().get(0).nodeId();
        this.paths.add(pathNodeId);
        List<NodeModel> next = taskNode.next();
        NodeModel nodeModel = next.get(0);
        submitNext(nodeModel);
    }

    private void submitNext(NodeModel nextModel){
        NodeType nodeType = nextModel.nodeType();
        switch (nodeType){
            case TASK:
                //任务节点
                createNewTask((TaskNode) nextModel);
                break;
            case DISC:
                //决策节点
                List<Path> pathList = nextModel.path();
                for(Path path : pathList){
                    String nodeId = path.getNodeId();
                    StoneProcessNodeVo pathNode = stoneProcessNodeVos.stream().filter(s->s.getNodeId().equals(nodeId)).findFirst().orElse(null);
                    if(!ObjectUtils.isEmpty(pathNode) && !ObjectUtils.isEmpty(pathNode.getDiscRules())){
                        String discRules = pathNode.getDiscRules();
                        Boolean discBool = NodeUtils.expressBool(this.param, discRules);
                        if(Boolean.TRUE.equals(discBool)){
                            this.paths.add(nodeId);
                            submitNext(path.getNextNode());
                        }
                    }
                }
                break;
            case FORK:
                //分支
                List<Path> paths = nextModel.path();
                List<String> submitPaths = this.workflowParam.getPathNodeId();
                for(Path path : paths){
                    NodeModel nextNode = path.getNextNode();
                    if(!ObjectUtils.isEmpty(submitPaths)){
                        if(submitPaths.contains(path.nodeId())) {
                            this.paths.add(path.nodeId());
                            submitNext(nextNode);
                        }
                    }else{
                        this.paths.add(path.nodeId());
                        submitNext(nextNode);
                    }
                }
                break;
            case JOIN:
                //聚合
                joinExecute((JoinNode) nextModel);
                break;
            case WAIT:
                //等待
                saveWaitTask((WaitNode) nextModel);
                break;
            case AUTO:
                //自动处理
                autoNodeExecute((AutoNode) nextModel);
                break;
            case END:
                //结束
                finishProcess((EndNode) nextModel);
                break;
            default:
                break;
        }
        if(Boolean.TRUE.equals(this.taskNextContinue)){
            //执行后续任务
            this.taskNextContinue = false;
            String pathNodeId = nextModel.path().get(0).getNodeId();
            this.paths.add(pathNodeId);
            List<NodeModel> nexts = nextModel.next();
            for(NodeModel next : nexts){
                submitNext(next);
            }
        }
    }


    /**
     * 聚合操作 如果聚合节点前面的所有任务节点都完成，则执行后续节点，如果存在未完成的节点，则不用继续往后执行
     */
    private void joinExecute(JoinNode joinNode){
        List<StoneTaskVo> tasks = getPreActiveTask(joinNode);
        if(ObjectUtils.isEmpty(tasks)){
            this.taskNextContinue = true;
        }
    }

    private List<StoneTaskVo> getPreActiveTask(NodeModel nodeModel){
        List<NodeModel> allPreTaskNode = NodeUtils.getAllPreTaskNode(nodeModel);
        if (ObjectUtils.isEmpty(allPreTaskNode)) {
            return new ArrayList<>();
        }
        List<String> nodeIds = allPreTaskNode.stream().map(NodeModel::nodeId).collect(Collectors.toList());
        LambdaQueryWrapper<StoneTask> wrapper = stoneTaskService.wrapper()
                .eq(StoneTask::getProcessInstanceId, processInstanceId)
                .in(StoneTask::getNodeId, nodeIds);
        return stoneTaskService.queryVoList(wrapper);
    }



    private void finishProcess(EndNode endNode){
        List<StoneTaskVo> tasks = getPreActiveTask(endNode);
        if(!ObjectUtils.isEmpty(tasks)){
            //如果还有存在的任务，则不往后执行
            return;
        }
        //结束节点到达事件
        eventExecute(endNode,EVENT_TASK_ARRIVE);

        //结束流程
        stoneProcessInstanceService.deleteObjById(instanceVo.getId());
        //保存结果表
        StoneProcessInstanceHistoryVo historyVo = stoneProcessInstanceHistoryService.getByProcessInstanceId(instanceVo.getProcessInstanceId());
        if(!ObjectUtils.isEmpty(historyVo)){
            historyVo.setStatus(WorkflowConstant.INSTANCE_STATUS_FINISH);
            historyVo.setFinishTime(new Date());
            historyVo.setExt(instanceExt());
            stoneProcessInstanceHistoryService.saveVo(historyVo);
        }
        //保存结束历史任务
        StoneTaskHistoryVo taskVo = new StoneTaskHistoryVo();
        taskVo.setProcessInstanceId(processInstanceId);
        taskVo.setTaskId(PlatUtils.uuid());
        taskVo.setExt(JsonHelper.toJson(this.param));
        taskVo.setStatus(TASK_STATUS_COMPLETE);
        taskVo.setOperateType(TASK_OPERATE_TYPE_AUTO_SUBMIT);
        taskVo.setNodeId(endNode.nodeId());
        taskVo.setNodeName(endNode.getNodeName());
        if(!ObjectUtils.isEmpty(this.paths)){
            String pathJoins = StringUtils.join(this.paths, ";");
            taskVo.setPath(pathJoins);
        }
        taskVo.setNodeType(endNode.nodeType().value());
        taskVo.setProcessId(processId);
        taskVo.setReceiveTime(new Date());
        taskVo.setExecuteTime(new Date());
        stoneTaskHistoryService.saveVo(taskVo);
        //结束节点完成事件
        eventExecute(endNode,EVENT_TASK_END);
        eventExecute(endNode, EventTimeEnum.EVENT_PROCESS_END.getType());
        //流程结束日志
        stoneProcessLogService.saveLog(processInstanceId,processId,endNode.nodeId(),endNode.getNodeName(),null,
                ProcessLogTypeEnum.SUBMIT.getType(),this.account,"流程已结束");
    }

    private void createNewTask(TaskNode newTaskNode){
        StoneProcessNodeVo nodeConfig = stoneProcessNodeVos.stream().filter(s -> s.getNodeId().equals(newTaskNode.getNodeId())).findFirst().orElse(null);
        //审批人数据
        List<NextNode> nextNodes = this.workflowParam.getNextNodes();
        NextNode nextNode = nextNodes.stream().filter(n -> n.getNodeId().equals(newTaskNode.nodeId())).findFirst().orElse(null);
        if(ObjectUtils.isEmpty(nextNode)){
            //没有获取到，跳过
            this.taskNextContinue = true;
            return;
        }
        LinkedList<NodeApproveUser> approveUsers = nextNode.getApproveUsers();
        if(ObjectUtils.isEmpty(approveUsers)){
            throw new WorkflowException(newTaskNode.nodeName()+" 审批人无法找到！");
        }
        //修改流程状态，合并表单数据
        if(!this.changeInstanceFlag) {
            instanceVo.setExt(instanceExt());
            instanceVo.setStatus(WorkflowConstant.INSTANCE_STATUS_PROCESSING);
            stoneProcessInstanceService.saveVo(instanceVo);

            StoneProcessInstanceHistoryVo instanceHistoryVo = stoneProcessInstanceHistoryService.getByProcessInstanceId(instanceVo.getProcessInstanceId());
            if(!ObjectUtils.isEmpty(instanceHistoryVo)){
                instanceHistoryVo.setStatus(WorkflowConstant.INSTANCE_STATUS_PROCESSING);
                instanceHistoryVo.setUpdateTime(new Date());
                stoneProcessInstanceHistoryService.saveVo(instanceHistoryVo);
            }
        }
        this.changeInstanceFlag = true;
        for(NodeApproveUser approveUser : approveUsers){
            StoneTaskVo taskVo = new StoneTaskVo();
            taskVo.setProcessInstanceId(processInstanceId);
            taskVo.setTaskId(PlatUtils.uuid());
            taskVo.setActorId(approveUser.getAccount());
            taskVo.setActorName(approveUser.getUsername());
            taskVo.setExt(JsonHelper.toJson(this.param));
            taskVo.setStatus(TASK_STATUS_PROCESSING);
            taskVo.setNodeId(newTaskNode.nodeId());
            taskVo.setNodeName(newTaskNode.getNodeName());
            taskVo.setNodeType(newTaskNode.nodeType().value());
            taskVo.setProcessId(processId);
            taskVo.setReceiveTime(new Date());
            if(!ObjectUtils.isEmpty(nodeConfig)){
                taskVo.setUrl(nodeConfig.getExecForm());
            }
            //路线
            if(!ObjectUtils.isEmpty(this.paths)){
                String pathJoins = StringUtils.join(this.paths, ";");
                taskVo.setPath(pathJoins);
            }
            stoneTaskService.saveVo(taskVo);
            //保存任务历史记录
            StoneTaskHistoryVo taskHistoryVo = BeanCopyUtils.copyAndInit(taskVo,StoneTaskHistoryVo.class);
            stoneTaskHistoryService.saveVo(taskHistoryVo);
        }
        //任务到达事件
        eventExecute(newTaskNode,EVENT_TASK_ARRIVE);
    }

    private String instanceExt(){
        String ext = instanceVo.getExt();
        if(!ObjectUtils.isEmpty(ext) && ext.startsWith("{")){
            try {
                Map<String, Object> map = JsonHelper.jsonToMap(ext);
                map.putAll(this.param);
                return JsonHelper.toJson(map);
            }catch (Exception e){
            }
        }
        return JsonHelper.toJson(this.param);
    }

    private void saveWaitTask(WaitNode waitNode){
        //任务到达回调
        eventExecute(waitNode,EVENT_TASK_ARRIVE);

        StoneTaskVo taskVo = new StoneTaskVo();
        taskVo.setProcessInstanceId(processInstanceId);
        taskVo.setTaskId(PlatUtils.uuid());
        taskVo.setExt(JsonHelper.toJson(this.param));
        taskVo.setStatus(TASK_STATUS_PROCESSING);
        taskVo.setNodeId(waitNode.nodeId());
        taskVo.setNodeName(waitNode.getNodeName());
        taskVo.setNodeType(waitNode.nodeType().value());
        taskVo.setProcessId(processId);
        taskVo.setReceiveTime(new Date());
        stoneTaskService.saveVo(taskVo);
        //保存任务历史记录
        StoneTaskHistoryVo taskHistoryVo = BeanCopyUtils.copyAndInit(taskVo,StoneTaskHistoryVo.class);
        stoneTaskHistoryService.saveVo(taskHistoryVo);
        //流程日志
        stoneProcessLogService.saveLog(processInstanceId,processId,waitNode.nodeId(),waitNode.getNodeName(),taskVo.getTaskId(),
                ProcessLogTypeEnum.SUBMIT.getType(),this.account,"流程进入等待节点【"+waitNode.nodeName()+"】");
    }

    /**
     * 自动处理节点 处理
     */
    private void autoNodeExecute(AutoNode autoNode){
        this.taskNextContinue = true;
        //任务到达回调
        eventExecute(autoNode,EVENT_TASK_ARRIVE);
        //保存历史任务
        saveAutoNodeTask(autoNode);
        //任务结束回调
        eventExecute(autoNode,EVENT_TASK_END);
    }

    private void eventExecute(NodeModel nodeModel,String eventTime){
        log.info("流程{}，执行自动处理前置回调",processInstanceId);
        //获取配置变量
        this.variableConfigs = stoneProcessVariableConfigService.getVariableConfigs(processId,nodeModel.nodeId());
        EventParam eventParam = new EventParam();
        eventParam.setProcessCode(processVo.getProcessCode());
        eventParam.setProcessId(processId);
        eventParam.setActorId(this.account);
        eventParam.setActorName(this.nowUser.getUsername());
        eventParam.setEventTime(eventTime);
        eventParam.setNodeId(nodeModel.nodeId());
        eventParam.setNodeName(nodeModel.nodeName());
        eventParam.setNodeType(nodeModel.nodeType().value());
        eventParam.setProcessInstanceId(instanceVo.getProcessInstanceId());
        eventParam.setFormData(this.param);
        if(nodeModel instanceof TaskModel){
            eventParam.setFirstTask(((TaskModel) nodeModel).isFirstTask());
        }
        eventParam.setVariableConfigs(variableConfigs);
        eventService.execute(eventParam);
    }

    private void saveAutoNodeTask(AutoNode autoNode){
        StoneTaskHistoryVo taskVo = new StoneTaskHistoryVo();
        taskVo.setProcessInstanceId(processInstanceId);
        taskVo.setTaskId(PlatUtils.uuid());
        taskVo.setExt(JsonHelper.toJson(this.param));
        taskVo.setStatus(TASK_STATUS_COMPLETE);
        taskVo.setOperateType(TASK_OPERATE_TYPE_AUTO_SUBMIT);
        taskVo.setNodeId(autoNode.nodeId());
        taskVo.setNodeName(autoNode.getNodeName());
        taskVo.setNodeType(autoNode.nodeType().value());
        taskVo.setProcessId(processId);
        taskVo.setReceiveTime(new Date());
        taskVo.setExecuteTime(new Date());
        stoneTaskHistoryService.saveVo(taskVo);

        //流程日志
        stoneProcessLogService.saveLog(processInstanceId,processId,autoNode.nodeId(),autoNode.getNodeName(),taskVo.getTaskId(),
                ProcessLogTypeEnum.SUBMIT.getType(),this.account,"节点【"+autoNode.nodeName()+"】自动处理成功");
    }


    private void completeNowTask(){
        //如果当前节点下一个节点不是合流也不是结束，则完成所有当前节点任务
        List<NodeModel> next = taskNode.next();
        NodeType nodeType = next.get(0).nodeType();
        //需要处理的任务
        List<StoneTaskVo> taskVos = new ArrayList<>();
        taskVos.add(this.taskVo);
        if(!NodeType.JOIN.equals(nodeType) && !NodeType.END.equals(nodeType)){
            LambdaQueryWrapper<StoneTask> wrapper = stoneTaskService.wrapper()
                    .eq(StoneTask::getProcessInstanceId, processInstanceId)
                    .eq(StoneTask::getNodeId, taskNode.nodeId())
                    .eq(StoneTask::getStatus, TASK_STATUS_PROCESSING);
            taskVos = stoneTaskService.queryVoList(wrapper);
        }
        for(StoneTaskVo task : taskVos){
            //保存历史任务
            StoneTaskHistoryVo historyVo = stoneTaskHistoryService.getByTaskId(task.getTaskId());
            historyVo.setExecuteTime(new Date());
            historyVo.setStatus(TASK_STATUS_COMPLETE);
            historyVo.setOperateType(TASK_OPERATE_TYPE_SUBMIT);
            if(this.taskVo.getTaskId().equals(task.getTaskId())) {
                historyVo.setActorId(account);
                historyVo.setActorName(nowUser.getUsername());
                historyVo.setSuggest(this.workflowParam.getSuggest());
            }
            stoneTaskHistoryService.saveVo(historyVo);
            //删除进行中任务
            stoneTaskService.deleteObjById(task.getId());
        }
        //执行当前任务完成回调
        eventExecute(taskNode,EVENT_TASK_END);
        //记录日志
        stoneProcessLogService.saveLog(processInstanceId,processId,taskVo.getNodeId(),taskVo.getNodeName(),taskVo.getTaskId(),
                ProcessLogTypeEnum.SUBMIT.getType(),this.account,"审批提交了任务，审批节点【"+taskNode.nodeName()+"】");
    }

    private void loadWorkflowDetail(){
        WorkflowTask details = BeanCopyUtils.copyAndInit(instanceVo,WorkflowTask.class);
        details.setProcessCode(processVo.getProcessCode());
        details.setProcessName(processVo.getProcessName());
        details.setInitAccount(instanceVo.getStarter());
        details.setInitUserName(instanceVo.getStarterName());
        this.result.setDetails(details);
    }
}
